package com.qf.vote2203.dbvote.service.impl;

import cn.hutool.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.vote2203.dbvote.entity.Vote;
import com.qf.vote2203.dbvote.mapper.VoteMapper;
import com.qf.vote2203.dbvote.service.IVoteService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.vote2203.utils.Constants;
import com.qf.vote2203.utils.LocalCache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author dong
 * @since 2023-02-27
 */
@Service
public class VoteServiceImpl extends ServiceImpl<VoteMapper, Vote> implements IVoteService {


    @Override
    public String getToken(String phone) throws Exception {

        //1. 检查一下该请求的 手机号是否 已经有可以使用的token （时间间隔在1分钟之内）
        // 内存中应该有一个map结构，  key为手机号， value为？ 应该包含token和上次创建的时间
        final Map<String, Object> jwtCache = LocalCache.jwtTokenCache.get(phone);

        if (jwtCache!=null){
            final Long lastTokenTime = (Long) jwtCache.get(Constants.JWT_CACHE_TIME_KEY);

            // 比对时间有没有超过 60s
            final long curTime = System.currentTimeMillis();

            if (curTime-lastTokenTime<=1000*60) {
                final String cachedToken = (String) jwtCache.get(Constants.JWT_CACHE_TOKEN_KEY);
                return cachedToken;
            }else { // 超过了60s，新创建一个token
                final String newToken = createNewToken(phone);

                // 将新创建的token加入缓存
                jwtCache.put(Constants.JWT_CACHE_TOKEN_KEY,newToken);
                jwtCache.put(Constants.JWT_CACHE_TIME_KEY,System.currentTimeMillis());
                LocalCache.jwtTokenCache.put(phone,jwtCache);

                return newToken;
            }


        }else {
            final String newToken = createNewToken(phone);
            // 把token添加到本地缓存中
            final HashMap<String, Object> cache = new HashMap<>();
            cache.put(Constants.JWT_CACHE_TOKEN_KEY,newToken);
            cache.put(Constants.JWT_CACHE_TIME_KEY,System.currentTimeMillis());
            LocalCache.jwtTokenCache.put(phone,cache);

            return newToken;
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void doVote(String phone, Integer cadidateId, String token) throws Exception {

        //1. 检查当前手机号是否能够发起投票
        checkVote(phone,token);

        //2. 进行投票业务逻辑处理
        //2.1 检查一下该用户是否为当前候选人透过票 ，投过票按照更新逻辑，没投过按新增逻辑

        final Vote dbVote = this.getOne(new QueryWrapper<Vote>().eq("phone", phone)
                .eq("candidate_id", cadidateId));

        if (dbVote!=null){
            // 更新投票的数量
            dbVote.setVoteNum(dbVote.getVoteNum()+1);
            dbVote.setUpdateTime(new Date());

            this.updateById(dbVote); // 根据ID进行更新

        }else {
            // 新增一条投票数据
            final Vote vote = new Vote();
            vote.setPhone(phone);
            vote.setCandidateId(cadidateId);
            vote.setUpdateTime(new Date());
            vote.setVoteNum(1);
            this.save(vote);
        }


    }

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public List<Map<String, Object>> statisticVote() {

        final List<Map<String, Object>> listMaps = this.listMaps(new QueryWrapper<Vote>().select("candidate_id,SUM(vote_num) AS totalnum")
                .groupBy("candidate_id"));

        // 处理listMaps中的数据，把 candate_id 转换成 候选人的名字


        return listMaps;
    }

    private void checkVote(String phone, String token) throws Exception {

        //1. 检查 token 的有效性
        final boolean validate = JWT.of(token).setKey(Constants.SECRET.getBytes())
                .validate(0);

        //2. 校验当前手机号在数据库中的上次最新投票时间是不是满足大于60秒的间隔
        if (validate){

            // 查询当前手机号在数据库中的上次最新投票时间
            final Object date = this.getObj(new QueryWrapper<Vote>().select("update_time").
                            eq("phone", phone)
                            .orderByDesc("update_time").last("limit 1")
                    , o -> o);

            if (date == null){
                // 该手机号从来没有投过票
                return;
            }

            Date d = (Date) date;
            final long dbUpdatetime = d.getTime();

            if (System.currentTimeMillis()- dbUpdatetime<=1000*60) {
                // 投票间隔不足
                throw new Exception("投票间隔不足1分钟");
            }


        }else {
            throw new Exception("token 无效");
        }


    }

    /**
     * 创建jwttoken
     * @param phone
     * @return
     */
    private String createNewToken(String phone) throws Exception {

        final String jwtToken = JWT.create().setSubject(phone)
                .setExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60))
                .setKey(Constants.SECRET.getBytes("utf-8"))
                .sign();

        return jwtToken;
    }
}
